#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "cJSON.h"

#define N 1024

typedef struct sockaddr SA;

char file_name[32];
int file_flag=0;//下发配置文件标志位

// 这个例子里模拟了两个端口的响应，第一个端口用8888，第二个用6666
// 模型用的select配合着线程实现的，仅作为参考

void *new_sock(void *arg)
{
    printf("will recv new connect\n");
    int sockfd, clientfd;
    char buf[N];
    int addrlen = sizeof(struct sockaddr);
    struct sockaddr_in addr, clientaddr;
    int nbytes;
    // 1创建一个套接字--socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        exit(-1);
    }
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    // 2定义套接字地址--sockaddr_in
    bzero(&addr, addrlen);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8878); 

    // 3绑定套接字--bind
    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        exit(-1);
    }

    // 4启动监听--listen
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        exit(-1);
    }

    // 5接收连接--accept
    clientfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
    if (clientfd < 0)
    {
        perror("accept err");
        exit(-1);
    }

    printf("recv new client\n");
    int ret;
    char fpbuf[2048] = {};
    FILE *fp = fopen("../config/config.json", "w+");
    if (fp == NULL)
    {
        perror("fp err\n");
        return -1;
    }
    printf("%s\n", fpbuf);
    // 6收发数据--recv/send
    while (1)
    {
        nbytes = recv(clientfd, fpbuf, 2048, 0);
        fpbuf[nbytes] = '\0'; // 确保字符串以 '\0' 结尾
        if (nbytes < 0)
        {
            perror("recv err");
            exit(-1);
        }
        else if (nbytes > 0)
        {
            fwrite(fpbuf, 1, nbytes, fp);
            fflush(NULL);
        }
        else if(file_flag==1&&nbytes<=0)
        {
            printf("peer exit\n");
            break;
        }
    }
    close(fp);
    close(clientfd);
    close(sockfd);
}

int main(int argc, char *argv[])
{
    // 创建用于广播套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) // 判错
    {
        perror("fd err");
        return -1;
    }
    int on = 1;
    // 设置套接字属性 允许发送
    setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
    struct sockaddr_in addr, dev_addr;
    int len = sizeof(addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8886);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(fd, (struct sockaddr *)&addr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    char buf[32];
    char jsonbuf[1024] = {};
    // while (1)
    // {
    recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&dev_addr, &len); // 拿到 ip，端口号
    printf("%s\n", buf);
    if (!strcmp(buf, "114514"))
    {
        sendto(fd, "114514", strlen("114514"), 0, (struct sockaddr *)&dev_addr, len);
        // break;
    }
    // 创建服务器等待连接 QT
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd < 0)
    {
        perror("tcpfd err");
        return -1;
    }
    if (bind(tcpfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("tcp bind err");
        return -1;
    }
    listen(tcpfd, 5);
    printf("wait client connect\n");

    // 3.更新最大文件描述符，并设置关注集
    int maxfd = tcpfd;
    fd_set rdfs, rdtmp;
    FD_ZERO(&rdfs);       // 清空关注列表
    FD_SET(tcpfd, &rdfs); // 将fd放入关注列表中

    while (1)
    {
        rdtmp = rdfs;
        int tcp_accpfd;
        if (select(maxfd + 1, &rdtmp, NULL, NULL, NULL) < 0)
        {
            perror("select err");
            return -1;
        }
        // 遍历集合，如果服务器句柄有数据，那么代表有连接进来，把它加入描述符集并更新maxfd
        // 否则代表客户端句柄有数据，那么接收数据，如果对端退出，那么把它从描述符集清除
        for (int i = 0; i < maxfd + 1; i++)
        {
            if (FD_ISSET(i, &rdtmp))
            {
                if (i == tcpfd)
                { // 表示有新连接进来
                    tcp_accpfd = accept(tcpfd, NULL, NULL);
                    FD_SET(tcp_accpfd, &rdfs);
                    if (tcp_accpfd > maxfd)
                    {
                        maxfd = tcp_accpfd;
                    }
                }
                else
                { // 客户端有数据
                    int n = recv(i, jsonbuf, N, 0);
                    if (n > 0)
                    {
                        cJSON *root = cJSON_Parse(jsonbuf);
                        printf("recv from %d--%s\n", i, jsonbuf); // 记得加\n冲刷缓冲区
                        cJSON *item = cJSON_GetObjectItem(root, "type");
                        if (item->valueint == 1)
                        {
                            const char *filename = "config.json";

                            if (access(filename, F_OK) == 0)
                            {
                                printf("is exit\n");
                                cJSON *rec = cJSON_CreateObject();
                                cJSON_AddItemToObject(rec, "type", cJSON_CreateNumber(1));
                                cJSON_AddItemToObject(rec, "result", cJSON_CreateNumber(0));
                                cJSON_AddItemToObject(rec, "up_config", cJSON_CreateString("false"));
                                char *val = cJSON_Print(rec);
                                send(tcp_accpfd, val, strlen(val), 0);
                            }
                            else
                            {
                                printf("not exit\n");
                                cJSON *rec = cJSON_CreateObject();
                                cJSON_AddItemToObject(rec, "type", cJSON_CreateNumber(1));
                                cJSON_AddItemToObject(rec, "result", cJSON_CreateNumber(0));
                                cJSON_AddItemToObject(rec, "up_config", cJSON_CreateString("true"));
                                char *val = cJSON_Print(rec);
                                printf("%s\n", val);
                                send(tcp_accpfd, val, strlen(val), 0);
                            }
                        }
                        else if (item->valueint == 2)
                        {
                            cJSON *data = cJSON_GetObjectItem(root, "data");
                            cJSON *flag = cJSON_GetObjectItem(data, "flag");
                            if (!strcmp(flag->valuestring, "start"))
                            {
                                printf("%s\n", flag->valuestring);
                                cJSON *filename = cJSON_GetObjectItem(data, "file_name");
                                cJSON *filelen = cJSON_GetObjectItem(data, "file_len");
                                strcpy(file_name, filename->valuestring);
                                printf("开始接收点表文件，文件大小%d\n", filelen->valueint);
                                // 新建一个新的服务器，这里如果还想保证8888的端口客户端正常响应，必须开线程，否则会阻塞到新的操作中
                                pthread_t tid;
                                pthread_create(&tid, NULL, new_sock, NULL);
                            }
                            if (!strcmp(flag->valuestring, "stop"))
                            {
                                file_flag = 1;
                                cJSON *root2 = cJSON_CreateObject();
                                cJSON_AddItemToObject(root2, "type", cJSON_CreateNumber(2));
                                cJSON *data2 = cJSON_CreateObject();
                                cJSON_AddItemToObject(data2, "flag", cJSON_CreateString("stop"));
                                cJSON_AddItemToObject(root2, "data", data2);
                                memset(jsonbuf, 0, sizeof(jsonbuf));
                                strcpy(jsonbuf, cJSON_Print(root2));
                                printf("send2:%s\n", jsonbuf);
                                send(i, jsonbuf, N, 0);
                            }
                        }
                        else if(item->valueint == 3)
                        {
                            cJSON *root3 = cJSON_CreateObject();
                            cJSON_AddItemToObject(root3, "type", cJSON_CreateNumber(3));
                            cJSON_AddItemToObject(root3, "result", cJSON_CreateNumber(0));
                            memset(jsonbuf, 0, sizeof(jsonbuf));
                            strcpy(jsonbuf, cJSON_Print(root3));
                            printf("send3:%s\n", jsonbuf);
                            send(i, jsonbuf, N, 0);
                        }
                        memset(jsonbuf,0,sizeof(jsonbuf));
                    }
                    else if (n == 0)
                    {
                        FD_CLR(i, &rdfs);
                        close(i);
                    }
                    else
                    {
                    }
                }
            }
        }
    }

    return 0;
}